package com.orm.mapping;

import com.orm.util.ClassUtil;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.lang.reflect.Parameter;
import java.util.*;

/**
 * 不知道起什么名字
 *
 * 从 参数映射map 中获取值
 *
 * 2个阶段都用到：  1.动态解析sql 节点时，需要判断 if 条件里面的值
 * 2.  #{name}  实际参数替换时，也需要判断 里面表达式的值
 *
 * 所以，专门集中到一个类里面来
 *
 * 另外，检查sql 语句合法性时，也用到，只是不检查值，而是检查 对应的参数名 或属性是否存在
 */
@Slf4j
public class MappingProcess {
	
	// 运行时取值
	// 至多允许一个. 的存在，不允许 [] 这种写法，不支持
	public static Object getElVal(Map<String, Object> paramNameToValue, String el) {
		if (el.indexOf('.') > -1) {
			
			// 有. 的情况, split[0] 视为参数名   split[1] 视为参数的属性
			
			String[] split = el.split("\\.");
			
			Object obj = paramNameToValue.get(split[0]);   //
			if (obj != null) {
				return ClassUtil.getVal(obj, split[1]);
			}
		} else {
			// 没有. 符号， 直接查找名字
			Object obj = paramNameToValue.get(el);
			if (obj != null) {
				return obj;
			} else {
				
				// 如果没有找到且参数列表中只有1个参数，将名字视为属性，再次查找
				if (paramNameToValue.size() == 1) {
					obj = paramNameToValue.values().toArray()[0];
					if (obj != null) {
						return ClassUtil.getVal(obj, el);
					}
				}
			}
		}
		return null;
	}
	
	
	// 分析sql ,sql 可以是片段 ，为了搜索到里面的 #{} 表达式
	public static void checkSql(Parameter[] parameters,String dynamicSql,String tips){
		// 从DefaultExecutor 复制过来的
		char[] chars = dynamicSql.toCharArray();
		for (int index = 0; index < chars.length; index++) {
			char c = chars[index];
			if (c == '#' && index + 1 < chars.length && chars[index + 1] == '{') {
				index += 2;             // 找到了 #{ 开头部分
				int index2 = index;
				while (index2 < chars.length) {
					char c2 = chars[index2];
					if (c2 == '}') {
						break;
					}
					if (c2 == '#' || c2 == '{') {
						throw new RuntimeException("#{}表达式错误");
					}
					index2++;
				}
				if (chars[index2] != '}') {
					throw new RuntimeException("#{}表达式错误");
				}
				
				String paramName = dynamicSql.substring(index, index2);
				
				checkEL(parameters,paramName,tips);
				
				index = index2;
			} else {
				if (c == '#' || c == '{' || c == '}') {
					throw new RuntimeException("#{}表达式错误");
				}
			}
		}
		// 能正常执行完就行
	}
	
	// 分析sql 时检查单个 表达式
	public static void checkEL(Parameter[] parameters, String el,String tips){
		if (parameters == null || parameters.length == 0) {
			throw new RuntimeException("方法参数信息为空，不能使用 #{} 方式填充数据");
		}
		
		// 如果某个el 找不到就抛异常
		if (el.indexOf('.') > -1) {
			// 有. 的情况, split[0] 视为参数名   split[1] 视为参数的属性
			String[] split = el.split("\\.");
			
			for (Parameter parameter : parameters) {
				if (parameter.getName().equals(split[0])) {
					Field[] declaredFields = parameter.getType().getDeclaredFields();
					for (Field field : declaredFields) {
						if (field.getName().equals(split[1])) {
							return;
						}
					}
				}
			}
		} else {
			// 没有. 符号
			for (Parameter parameter : parameters) {
				if (parameter.getName().equals(el)) {
					return;
				}
			}
			
			// 能到这里说明没能直接找到，后面再判断一次是否只有1个参数的情况
			if (parameters.length == 1) {
				// 只有1个参数情况下, 进入参数内部找属性
				Field[] declaredFields = parameters[0].getType().getDeclaredFields();
				for (Field field : declaredFields) {
					if (field.getName().equals(el)) {
						return;
					}
				}
			}
		}
		log.error("在分析 {} 对应的sql语句时发现错误, 表达式 {} 的值无法在参数中查到",tips,el);
		throw new RuntimeException("Sql 语句中 " + el + " 无法在参数列表中找到对应数据");
	}
}
